#include <iostream>
#include "../src/estructuraDatos/lista/Lista.h"
using namespace std;
#include <string>

/**
 * Clase abstracta, que define la interfaz de todos las Pruebas.
 * 
 */
class Prueba {
	public:
		virtual void ejecutar() {
            cout<< "Comenzando Prueba: " << this->getNombre()<<endl;
            this->comenzar();
            cout << "Prueba Finalizada" <<endl;
		}

    protected:
        
        /**
         * Metodo abstracto que sera implementado por los descendientes con
         * el conjunto de pasos de la Prueba.
         */
        virtual void comenzar() = 0;

        virtual string getNombre() = 0;
};

class PruebaListaEnteros: public Prueba {
	public :
		//imprimi la lista de enteros desde la posicion de donde esta el iterador
		//hasta el ultimo elemento de la lista
		void imprimirLista(IteradorLista<int> unIt){
			while (unIt.tieneSiguiente())
				cout<<unIt.siguiente()<<" ";
			cout<<endl;
		}
	protected:
		virtual void comenzar() {
            Lista<int> listaEnteros;
            cout << "tamanio: "<< listaEnteros.tamanio()<<endl;
            listaEnteros.agregar(1);
            listaEnteros.agregar(2);
            listaEnteros.agregar(3);
            
            cout<< " Se agregaron elementos, tamanio: "<< listaEnteros.tamanio()<<endl;

            cout << "Crea dos iteradores"<<endl;
            IteradorLista<int> iterador1 = listaEnteros.iterador();
            IteradorLista<int> iterador2 = listaEnteros.iterador();

            if (iterador1.tieneSiguiente())
                cout << "Obtiene un elemento con el primer iterador: "<<iterador1.siguiente()<<endl;

            cout<<"elementos obtenidos con el segundo iterador: "<<endl;
            this->imprimirLista(iterador2);

            cout<<"elementos obtenidos con el primer iterador: "<<endl;
            this->imprimirLista(iterador1);
        }

        virtual string getNombre() {
            return "Lista de Enteros";
        }
        
};

class PruebaListaStrings: public Prueba {
	public :
		//imprimi la lista de Strings desde la posicion de donde esta el iterador
		//hasta el ultimo elemento de la lista
		void imprimirLista(IteradorLista<string> unIt){
			while (unIt.tieneSiguiente())
				cout<<unIt.siguiente()<<" ";
			cout<<endl;
		}
    protected:
        virtual void comenzar() {
            Lista<string> listaStrings;
            cout<<"tamanio: "<<listaStrings.tamanio()<<endl;
            
            listaStrings.agregar("Primer elemento");
            listaStrings.agregar("Segundo elemento");
            listaStrings.agregar("Tercer elemento");
            listaStrings.agregar("Cuarto elemento");
            cout << "Se agregados elemtos, tamanio: "<<listaStrings.tamanio()<<endl;
            
            IteradorLista<string> iterador = listaStrings.iterador();
            imprimirLista(iterador);
        }

        virtual string getNombre() {
            return "Lista de Strings";
        }
};

class PruebaListaPunterosEnteros: public Prueba {
	public:
		void imprimirLista(IteradorLista<int*> unIt){
			while (unIt.tieneSiguiente())
				cout<<*unIt.siguiente()<<" ";
			cout<<endl;
		}

    protected:
        virtual void comenzar() {
            Lista<int*> lista;
            cout<<"tamanio: "<<lista.tamanio()<<endl;
            
            int* a = new int(3);
            int* b = new int(2);
            int* c = new int(1);
            lista.agregar(a);
            lista.agregar(b);
            lista.agregar(c);
            cout<<"Se agregaron elementos, tamanio: "<<lista.tamanio()<<endl;

            IteradorLista<int*> iterador = lista.iterador();
            imprimirLista(iterador);

            delete a;
            delete b;
            delete c;
        }
        
        virtual string getNombre() {
            return "Lista de Punteros a Entero";
        }
};

class PruebaListaIteraciones: public Prueba {
	public:
		void imprimirListaProgresivamente(IteradorLista<char> unIt) {
			while (unIt.tieneSiguiente())
				cout << unIt.siguiente() << " ";
			cout<<endl;
		}
		void imprimirListaRegresivamente(IteradorLista<char> unIt) {
			while (unIt.tieneAnterior())
				cout << unIt.anterior() << " ";
			cout<<endl;
		}
    protected:
        void mostrar(Lista<char>& lista) {
            IteradorLista<char> iterador = lista.iterador();
            while (iterador.tieneSiguiente())
                cout << iterador.siguiente() << " ";
            cout<<endl;
        }
        
        virtual void comenzar() {
            Lista<char> lista;
            for (char caracter = 'A'; caracter <= 'Z'; caracter++)
                lista.agregar(caracter);
            
            IteradorLista<char> iterador = lista.iterador();
            cout<<"impresion 1"<<endl;
            imprimirListaProgresivamente(iterador);
            cout<<"impresion 2"<<endl;
            imprimirListaRegresivamente(iterador);
            
            iterador.primero();
            cout<<"impresion 3"<<endl;
            imprimirListaProgresivamente(iterador);
            cout<<"impresion 4"<<endl;
            imprimirListaRegresivamente(iterador);  //no me funciona por ejemplo esta impresion
            
            iterador.ultimo();
            cout<<"impresion 5"<<endl;
            imprimirListaRegresivamente(iterador);
            cout<<"impresion 6"<<endl;
            imprimirListaProgresivamente(iterador);

            this->mostrar(lista);
            
            iterador.primero();
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "cambiar->" << iterador.get();
            iterador.set('1');
            std::cout << " por->" << iterador.get() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "cambiar->" << iterador.get();
            iterador.set('2');
            std::cout << " por->" << iterador.get() << std::endl;
            std::cout << "cambiar->" << iterador.siguiente() << std::endl;
            std::cout << "cambiar->" << iterador.get();
            iterador.set('3');
            std::cout << " por->" << iterador.get() << std::endl;
            std::cout << "anterior->" << iterador.anterior() << std::endl;
            std::cout << "anterior->" << iterador.anterior() << std::endl;
            std::cout << "remover->" << iterador.remover() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            std::cout << "siguiente->" << iterador.siguiente() << std::endl;
            
            iterador.insertar('@');
            std::cout << "insertar->" << iterador.get() << std::endl;
            
            this->mostrar(lista);
            
        }
        
        virtual string getNombre() {
            
            return "Iteraciones complejas sobre la lista";
        }
};

/**
 * Prueba de diferentes comparadores para ordenar y buscar elementos en la
 * Lista. 
 */
class PruebaComparadores: public Prueba {
    protected:
        void mostrar(Lista<int>& lista) {
            
            IteradorLista<int> iterador = lista.iterador();
            while (iterador.tieneSiguiente())
                cout << iterador.siguiente() << " ";
            cout << std::endl;
        }

        virtual void comenzar() {
            
            Lista<int> lista;
            lista.agregar(2);
            lista.agregar(1);
            lista.agregar(4);
            lista.agregar(6);
            lista.agregar(5);
            lista.agregar(10);
            lista.agregar(1);
            lista.agregar(9);
            lista.agregar(9);            
            lista.agregar(8);
            lista.agregar(7);
            lista.agregar(3);
            lista.agregar(1);
            
            this->mostrar(lista);
            
            Lista<int> listaAscendente(new ComparadorSimpleAscendente<int>());
            IteradorLista<int> iterador = lista.iterador();
            while (iterador.tieneSiguiente()) {
                
                listaAscendente.agregar(iterador.siguiente());
            }
            cout<<"lista ordenada ascendentemente"<<endl;
            this->mostrar(listaAscendente);
            Lista<int> listaDescendente(new ComparadorSimpleDescendente<int>());
            iterador.primero();
            while (iterador.tieneSiguiente()) {
                
                listaDescendente.agregar(iterador.siguiente());
            }
            cout<<"lista ordenada ascendentemente"<<endl;
            this->mostrar(listaDescendente);
            
            cout << listaAscendente.buscar(2).get() << std::endl;
            
        }            
        virtual string getNombre() {
            return "Comparadores";
        }

};


/**
 * Prueba generar errores al operar con la lista
 */
class PruebaErrores: public Prueba {
    protected:
        virtual void comenzar() {
            try {
                Comparador<string>*
                    comparador = new ComparadorSimpleDescendente<string>();
                
                Lista<string> lista(comparador);
                
                lista.agregar("Algoritmos");
                lista.agregar("Objetos");
                lista.agregar("Templates");
                lista.agregar("Excepciones");
                
                IteradorLista<string> iterador = lista.iterador();
                cout<<"lista ordenada ascendentemente"<<endl;
                while (iterador.tieneSiguiente())
                    std::cout << iterador.siguiente() << " ";
                cout<<endl;
                
                string buscado = "Objetos";
                
                cout << "Buscando " <<buscado<<endl;
                cout<<"encontro: "<<lista.buscar(buscado).get()<<endl;
                
                buscado = "Error";
                //pero si quiero buscar la ultima palabras, tambien me va a probocar un error
                IteradorLista<string> iteradorBusqueda = lista.buscar(buscado);
                if (iteradorBusqueda.tieneSiguiente()){
                    cout << "Buscando " << buscado <<endl;
                    cout<<" encontro: "<< iteradorBusqueda.get()<<endl;
                } else
                    cout << "Buscando " << buscado << " no se encontro"<<endl;
                
                /* esta l�nea deber�a provocar un error */
                cout << "Encontro: "<< lista.buscar(buscado).get()<<endl;
            
            } catch (ElementoInexistenteException error) {
                cout << "Se provoco un error: "<< error.getMensaje()<<endl;
            }
        }
        
        virtual std::string getNombre() {
            return "Errores";
        }        
};

//no hay perdida de memoria
void iniciarTestListas() {
    cout<<"inicia el test del manejo de listas"<<endl;
    Prueba** pruebas = new Prueba*[2];
    
    pruebas[0] = new PruebaListaEnteros();
    pruebas[1] = new PruebaListaStrings();
    
    
    for (int i = 0; i < 2; i++) {
        
        pruebas[i]->ejecutar();
        
        delete pruebas[i];
    }
    
    delete [] pruebas;
    
    
    /* Lista de punteros a Prueba (Prueba*) */
    Lista<Prueba*> listaPruebas;
    
    listaPruebas.agregar(new PruebaListaEnteros());
    listaPruebas.agregar(new PruebaListaStrings());
    listaPruebas.agregar(new PruebaListaPunterosEnteros());
    listaPruebas.agregar(new PruebaListaIteraciones());
    listaPruebas.agregar(new PruebaComparadores());
    listaPruebas.agregar(new PruebaErrores());

    IteradorLista<Prueba*> iterador = listaPruebas.iterador();
    while (iterador.tieneSiguiente()) {
        
        iterador.siguiente()->ejecutar();
    }
    
    /* recorre la lista retirando y destruyendo los elementos */
    iterador.primero();
    while (iterador.tieneSiguiente()) {

        Prueba* prueba = iterador.remover();
        delete prueba;
    }

}
